<template>
    <div class="vue-avatar--wrapper" :style="[style, customStyle!]" aria-hidden="true">
        <!-- this img is not displayed; it is used to detect failure-to-load of div background image -->
        <img v-if="isImage" style="display: none" :src="src" @error="onImgError" />
        <span v-show="!isImage">{{ userInitial }}</span>
    </div>
</template>

<script lang="ts" setup>
import { StyleValue } from 'vue';

const getInitials = (username: string) => {
    let parts = username.split(/[ -]/);
    let initials = '';

    for (var i = 0; i < parts.length; i++) {
        initials += parts[i].charAt(0);
    }

    if (initials.length > 3 && initials.search(/[A-Z]/) !== -1) {
        initials = initials.replace(/[a-z]+/g, '');
    }

    initials = initials.substr(0, 3).toUpperCase();

    return initials;
};

interface DefineProps {
    username: string;
    initials?: string;
    backgroundColor?: string;
    color?: string;
    inline?: boolean;
    customStyle?: StyleValue;
    size?: number;
    src?: string;
    rounded?: boolean;
    lighten?: number;
}

const props = withDefaults(defineProps<DefineProps>(), {
    size: 32,
    rounded: true,
    lighten: 80
});

const emit = defineEmits(['avatar-initials']);

const backgroundColors = ref([
    '#F44336',
    '#FF4081',
    '#9C27B0',
    '#673AB7',
    '#3F51B5',
    '#2196F3',
    '#03A9F4',
    '#00BCD4',
    '#009688',
    '#4CAF50',
    '#8BC34A',
    '#CDDC39',
    '#FFC107',
    '#FF9800',
    '#FF5722',
    '#795548',
    '#9E9E9E',
    '#607D8B'
]);
const imgError = ref(false);

const style = computed(() => {
    const style = {
        display: props.inline ? 'inline-flex' : 'flex',
        width: `${props.size}px`,
        height: `${props.size}px`,
        borderRadius: props.rounded ? '50%' : 0,
        lineHeight: `${props.size + Math.floor(props.size / 20)}px`,
        fontWeight: 'bold',
        alignItems: 'center',
        justifyContent: 'center',
        textAlign: 'center',
        userSelect: 'none'
    };

    const imgBackgroundAndFontStyle = {
        background: `transparent url('${props.src}') no-repeat scroll 0% 0% / ${props.size}px ${props.size}px content-box border-box`
    };

    const initialBackgroundAndFontStyle = {
        backgroundColor: background.value,
        font: `${Math.floor(props.size / 2)}px/${props.size}px Helvetica, Arial, sans-serif`,
        color: fontColor.value
    };

    const backgroundAndFontStyle = isImage.value
        ? imgBackgroundAndFontStyle
        : initialBackgroundAndFontStyle;

    Object.assign(style, backgroundAndFontStyle);
    return style as StyleValue;
});

const background = computed(() => {
    if (!isImage.value) {
        return (
            props.backgroundColor ||
            randomBackgroundColor(props.username.length, backgroundColors.value)
        );
    }
});

const isImage = computed(() => {
    return !imgError.value && Boolean(props.src);
});

const fontColor = computed(() => {
    if (!isImage.value) {
        return props.color || lightenColor(background.value!, props.lighten);
    }
});

const userInitial = computed(() => {
    if (!isImage.value) {
        const initials = getInitials(props.initials || props.username);
        return initials;
    }
    return '';
});

const onImgError = () => {
    imgError.value = true;
};

const randomBackgroundColor = (seed: number, colors: string[]) => {
    return colors[seed % colors.length];
};

const lightenColor = (hex: string, amt: number) => {
    let usePound = false;

    if (hex[0] === '#') {
        hex = hex.slice(1);
        usePound = true;
    }

    let num = parseInt(hex, 16);
    let r = (num >> 16) + amt;

    if (r > 255) r = 255;
    else if (r < 0) r = 0;

    let b = ((num >> 8) & 0x00ff) + amt;

    if (b > 255) b = 255;
    else if (b < 0) b = 0;

    let g = (num & 0x0000ff) + amt;

    if (g > 255) g = 255;
    else if (g < 0) g = 0;

    return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16);
};

onMounted(() => {
    if (!isImage.value) {
        emit('avatar-initials', props.username, userInitial);
    }
});
</script>
